#include "Setting.h"
#include <atldef.h>
#include <atlbase.h>
#include <atlapp.h>

CSetting* CSetting::m_pInstance = NULL;
static const char *INI_FILE_NAME	= "settings.ini";
static const char *INI_APP_NAME		= "uploader";


CSetting::CSetting(void)
	: m_listenLevel (0)
{
	char buf[MAX_PATH];
	GetModuleFileNameA(NULL, buf, MAX_PATH);
	m_settingFile = buf;
	size_t offset = m_settingFile.find_last_of('\\');
	ATLASSERT (offset != std::string::npos);
	m_settingFile = m_settingFile.substr(0, offset + 1);
	m_settingFile += INI_FILE_NAME;

	_LoadSetting();
}

CSetting::~CSetting(void) {
}

void CSetting::_LoadDefault() {
	// fill the default values
	m_def["currDir"] = "";
	m_def["lang"] = "zhs";
	m_def["proxy"] = "";
	m_def["thread using"] = "5";

	m_map = m_def;
}


void CSetting::_LoadSetting() {

	_LoadDefault();

	// load from .ini
	char buf[256];
	int count = 0;
	for (MapType::iterator it = m_map.begin(); it != m_map.end(); ++ it) {
		const char *key = it->first.c_str();
		if (GetPrivateProfileStringA(INI_APP_NAME, key, NULL, buf, 256, m_settingFile.c_str()) > 0) {
			// TODO: Check the validation
			it->second = buf;
			count ++;
		}
	}

	ATLTRACE(_T("Get %d profile items\n"), count);
}

bool CSetting::_SaveSetting() {
	if (m_changed.size() == 0) {
		return true;
	}

	bool ret = true;
	for (MapType::iterator it = m_changed.begin(); it != m_changed.end(); ++ it) {
		const std::string key = it->first;
		const std::string value = it->second;

		if (WritePrivateProfileStringA(INI_APP_NAME, key.c_str(), value.c_str(), m_settingFile.c_str()) == 0) {
			ret = false;
		}
	}

	if (ret) {
		m_changed.erase(m_changed.begin(), m_changed.end());
	}

	return ret;
}


bool CSetting::_CheckDigit(const std::string &str) const {
	size_t len = str.length();
	for (size_t i = 0; i < len; ++ i) {
		if (!isdigit(str[i])) {
			return false;
		}
	}
	return true;
}





CSetting* CSetting::getInstance() {
	if (m_pInstance == NULL) {
		m_pInstance = new CSetting();
	}

	return m_pInstance;
}


bool CSetting::save() {
	return _SaveSetting();
}


bool CSetting::isKeyExists(const std::string &key) const {
	return m_map.find(key) != m_map.end();
}


std::string CSetting::getValue(const std::string &key) const {
	ATLASSERT (m_map.find(key) != m_map.end());

	return m_map.find(key)->second;
}

void CSetting::setValue(const std::string &key, const std::string &value) {
	ATLASSERT (m_map.find(key) != m_map.end());

	if (m_map[key] != value) {
		const std::string oldValue = m_map[key];
		m_map[key] = value;

		// trigger the listener
		++ m_listenLevel;
		ListenerContainer::iterator it;
		for (it = m_listeners.begin(); it != m_listeners.end(); ++ it) {
			(*it)->onSettingChange(key, oldValue);
		}
		-- m_listenLevel;

		m_changed[key] = value;
	}
}


// helper
int _hex_to_int (char c) {
	if (c >= '0' && c <= '9') {
		return (int)c - '0';
	} else if (c >= 'A' && c <= 'F') {
		return c - 'A' + 10;
	} else if (c >= 'a' && c <= 'f') {
		return c - 'a' + 10;
	} else {
		ATLASSERT (false);
		return -1;
	}
}

COLORREF CSetting::_ParseColor(const std::string &strColor) const{
	ATLASSERT (strColor[0] == '#' && strColor.length() == 7);

	int r, g, b;
	r = (_hex_to_int(strColor[1]) << 4) | _hex_to_int(strColor[2]);
	g = (_hex_to_int(strColor[3]) << 4) | _hex_to_int(strColor[4]);
	b = (_hex_to_int(strColor[5]) << 4) | _hex_to_int(strColor[6]);

	return RGB(r, g, b);
}


COLORREF CSetting::getColor(const std::string &key) const{
	return _ParseColor(getValue(key));
}

void CSetting::setColor(const std::string &key, COLORREF value) {
	char buf[8];
	int r = GetRValue(value);
	int g = GetGValue(value);
	int b = GetBValue(value);
	sprintf_s(buf, 8, "#%02x%02x%02x", r, g, b);
	setValue(key, buf);
}


int CSetting::getInt(const std::string &key) const{
	std::string value = getValue(key);
	ATLASSERT(_CheckDigit(value));
	return atoi(value.c_str());
}

void CSetting::setInt(const std::string &key, int value) {
	char buf[10];
	sprintf_s(buf, 10, "%d", value);
	setValue(key, buf);
}

void CSetting::getPadding(int padding[4], const std::string &key) const {
	std::string strPadding = getValue(key);
	ATLASSERT (std::count(strPadding.begin(), strPadding.end(), ' ') == 3);
	const char *p = strPadding.c_str();
	int index = 0;
	padding[index ++] = atoi(p);
	while (index < 4) {
		p = strpbrk(p, " ");
		ATLASSERT (p != NULL);
		p ++;
		padding[index ++] = atoi(p);
	}
}

void CSetting::setPadding(const std::string &key, int padding[4]) {
	char buf[64];
	sprintf_s(buf, 64, "%d %d %d %d", padding[0], padding[1], padding[2], padding[3]);
	setValue(key, buf);
}


bool CSetting::addListener(IListener *listener) {
	ATLASSERT (m_listenLevel == 0);
	if (m_listenLevel > 0) {
		return false;
	}
	ATLASSERT (listener != NULL);
	ATLASSERT (m_listeners.find(listener) == m_listeners.end());

	m_listeners.insert(listener);
	return true;
}

bool CSetting::removeListener(IListener *listener) {
	ATLASSERT (m_listenLevel == 0);
	if (m_listenLevel > 0) {
		return false;
	}
	ATLASSERT (listener != NULL);
	ATLASSERT (m_listeners.find(listener) != m_listeners.end());
	
	m_listeners.erase(m_listeners.find(listener));
	return true;
}